// Xampler, a MIDI-driven sampler
// Copyright (C) 2006 Nick Thomas
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
// 02110-1301 USA

#include "MainWindow.hpp"
#include "SynthMessage.hpp"
#include <gtkmm/aboutdialog.h>
#include <gtkmm/filechooserdialog.h>
#include <gtkmm/messagedialog.h>
#include <gtkmm/stock.h>
#include <list>
using namespace xampler;

MainWindow::MainWindow()
    : m_top_level_vbox(false, 2),
      m_instrument_selector_hbox(false, 2),
      m_instrument_name_hbox(false, 6),
      m_instrument_name_label("Instrument Name:", Gtk::ALIGN_RIGHT)
{
    Glib::signal_timeout().connect(
        sigc::mem_fun(this, &MainWindow::synth_message_handler),
        SYNTH_MESSAGE_POLL_INTERVAL);

    set_title("Xampler");
    add_accel_group(m_menu_bar.get_accel_group());

    m_menu_bar.signal_quit().connect(
        sigc::mem_fun(this, &Gtk::Widget::hide));
    m_menu_bar.signal_about().connect(
        sigc::mem_fun(this, &MainWindow::on_about));
    m_menu_bar.signal_open().connect(
        sigc::mem_fun(this, &MainWindow::on_open));
    m_menu_bar.signal_revert().connect(
        sigc::mem_fun(this, &MainWindow::on_revert));
    m_menu_bar.signal_save().connect(
        sigc::mem_fun(this, &MainWindow::on_save));
    m_menu_bar.signal_save_as().connect(
        sigc::mem_fun(this, &MainWindow::on_save_as));

    for (int i = 0; i < MAX_INSTRUMENTS; i++) {
        Instrument &instrument = g_state->get_instrument(i);

        m_instrument_buttons[i].set_instrument_number(i);
        m_instrument_selector_hbox.add(m_instrument_buttons[i]);
        instrument.signal_name_changed().
            connect(sigc::mem_fun(m_instrument_buttons[i],
                                  &Gtk::ToggleButton::set_label));
        m_instrument_buttons[i].signal_selected().
            connect(sigc::mem_fun(this,
                                  &MainWindow::set_current_instrument));
        m_instrument_buttons[i].set_label(instrument.get_name());
    }

    m_current_instrument = 1;
    set_current_instrument(0);

    m_instrument_selector.set_shadow_type(Gtk::SHADOW_NONE);
    m_instrument_selector.set_policy(Gtk::POLICY_ALWAYS,
                                     Gtk::POLICY_NEVER);
    m_instrument_selector.add(m_instrument_selector_hbox);

    m_instrument_name_entry.signal_changed().connect(
        sigc::mem_fun(this, &MainWindow::set_instrument_name_from_entry));

    m_instrument_name_hbox.pack_start(m_instrument_name_label,
                                      Gtk::PACK_SHRINK);
    m_instrument_name_hbox.pack_start(m_instrument_name_entry,
                                      Gtk::PACK_EXPAND_WIDGET);

    m_top_level_vbox.pack_start(*m_menu_bar.get_menu_bar(), Gtk::PACK_SHRINK);
    m_top_level_vbox.pack_start(m_instrument_selector, Gtk::PACK_SHRINK);
    m_top_level_vbox.pack_start(m_instrument_name_hbox, Gtk::PACK_SHRINK);
    m_top_level_vbox.pack_start(m_instrument_editor, Gtk::PACK_EXPAND_WIDGET);

    add(m_top_level_vbox);
    resize(600, 600);
    show_all_children();
}

void MainWindow::set_current_instrument(int number)
{
    if (m_current_instrument != number) {
        m_instrument_buttons[m_current_instrument].set_active(false);
        m_current_instrument = number;
        m_instrument_buttons[m_current_instrument].set_active(true);
        update_instrument_display();
    }
}

void MainWindow::update_instrument_display()
{
    Instrument &instrument = g_state->get_instrument(m_current_instrument);
    m_instrument_name_entry.set_text(instrument.get_name());
    m_instrument_editor.set_current_instrument(m_current_instrument);
}

void MainWindow::set_instrument_name_from_entry()
{
    g_state->get_instrument(m_current_instrument).set_name(
        m_instrument_name_entry.get_text());
}

InstrumentButton::InstrumentButton()
{
    signal_toggled().
        connect(sigc::mem_fun(this, &InstrumentButton::emit_selected));
}

void InstrumentButton::emit_selected()
{
    m_signal_selected.emit(m_instrument_number);
}

void MainWindow::on_about()
{
    Gtk::AboutDialog dialog;

    std::list<Glib::ustring> authors;
    authors.push_back("Nick Thomas");
    dialog.set_authors(authors);

    dialog.set_comments("A MIDI-driven sampler.");
    dialog.set_copyright("Copyright (C) 2006 Nick Thomas");
    dialog.set_website("http://code.google.com/p/xampler/");

    dialog.set_license(
    "This program is free software; you can redistribute it and/or modify\n"
    "it under the terms of the GNU General Public License as published by\n"
    "the Free Software Foundation; either version 2 of the License, or\n"
    "(at your option) any later version.\n"
    "\n"
    "This program is distributed in the hope that it will be useful,\n"
    "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
    "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
    "GNU General Public License for more details.\n"
    "\n"
    "You should have received a copy of the GNU General Public License\n"
    "along with this program; if not, write to the Free Software Foundation,\n"
    "Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n");
    dialog.set_wrap_license(false);

    dialog.run();
}

bool MainWindow::synth_message_handler()
{
    SynthMessage msg;

    while (g_synth_message_buffer->read(&msg)) {
        m_instrument_editor.handle_synth_message(msg);
    }

    return true;
}

void MainWindow::on_open()
{
    Glib::ustring filename;

    if (get_save_filename("Choose a file to open",
                          filename, Gtk::FILE_CHOOSER_ACTION_OPEN)) {
        actually_load(filename);
    }
}

void MainWindow::on_revert()
{
    if (!m_last_saved_filename.empty()) {
        actually_load(m_last_saved_filename);
    } else {
        Gtk::MessageDialog dialog("You have no save file to revert to!");
        dialog.run();
    }
}

void MainWindow::on_save()
{
    Glib::ustring filename;
    bool err = false;

    if (!m_last_saved_filename.empty()) {
        filename = m_last_saved_filename;
    } else {
        err = !get_save_filename("Choose a file to save to", filename,
                                 Gtk::FILE_CHOOSER_ACTION_SAVE);
    }

    if (!err) {
        actually_save(filename);
    }
}

void MainWindow::on_save_as()
{
     Glib::ustring filename;

     if (get_save_filename("Choose a file to save to", filename,
                           Gtk::FILE_CHOOSER_ACTION_SAVE)) {
         actually_save(filename);
     }
}

bool MainWindow::get_save_filename(Glib::ustring title,
                                   Glib::ustring &filename,
                                   Gtk::FileChooserAction action)
{
    Gtk::FileChooserDialog file_chooser(title, action);
    file_chooser.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
    file_chooser.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);

    Gtk::FileFilter filter_xampler;
    filter_xampler.set_name("Xampler files (*.xmplr)");
    filter_xampler.add_pattern("*.xmplr");
    file_chooser.add_filter(filter_xampler);

    Gtk::FileFilter filter_any;
    filter_any.set_name("All files");
    filter_any.add_pattern("*");
    file_chooser.add_filter(filter_any);

    int result = file_chooser.run();

    switch (result) {
    case Gtk::RESPONSE_OK:
        filename = file_chooser.get_filename();
        m_last_saved_filename = filename;
        return true;
    case Gtk::RESPONSE_CANCEL:
    default:
        return false;
    }
}

void MainWindow::actually_save(Glib::ustring filename)
{
    Glib::ustring error;

    if (!g_state->save(filename, error)) {
        Gtk::MessageDialog error_dialog("Error while saving",
                                        false, Gtk::MESSAGE_ERROR);
        error_dialog.set_secondary_text(error);
        error_dialog.run();
    }
}

void MainWindow::actually_load(Glib::ustring filename)
{
    Glib::ustring error;

    if (!g_state->load(filename, error)) {
        Gtk::MessageDialog error_dialog("Error while opening",
                                        false, Gtk::MESSAGE_ERROR);
        error_dialog.set_secondary_text(error);
        error_dialog.run();
    } else {
        update_instrument_display();
    }
}
